package org.nicocube.airain.domain.server.storable;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.nicocube.airain.domain.client.character.Gender;
import org.nicocube.airain.domain.client.dao.NoPlayerException;
import org.nicocube.airain.domain.client.exception.DomainException;
import org.nicocube.airain.domain.client.exception.GCMaxNumberCreateException;
import org.nicocube.airain.domain.client.exception.GCNameDuplicateCreateException;
import org.nicocube.airain.domain.client.gamedate.GameDate;
import org.nicocube.airain.domain.client.gamedate.GameDateManager;
import org.nicocube.airain.domain.server.dao.DomainDAO;
import org.nicocube.airain.domain.server.dao.DomainQuery;
import org.nicocube.airain.domain.server.dao.DomainRequest;
import org.nicocube.airain.domain.server.dao.DomainResultContainer;
import org.nicocube.airain.domain.server.dao.PersistenceAccessor;

import com.google.appengine.api.users.User;

public class GameCharacterStorableDAO extends DomainDAO {

	private GameDateManager gdm;
	private PlayerDAO playerDAO;

	public GameCharacterStorableDAO() throws DomainException {
		gdm = getDao(GameDateManagerDAO.class).getSingleton();
		playerDAO = getDao(PlayerDAO.class);
	}
	
	/**
	 * Retrieve all the {@link GameCharacterStorable} for the given {@link User} object
	 * 
	 * @param user
	 * @return
	 * @throws DomainException
	 */
	public List<GameCharacterStorable> getAllByOwner(final User user) throws DomainException {
		final List<GameCharacterStorable> result = new ArrayList<GameCharacterStorable>();

		final DomainResultContainer<Long> playerId = new DomainResultContainer<Long>();
		eval(
			new DomainRequest() {
				@Override
				public void playOn(PersistenceAccessor pm)
						throws DomainException {
					DomainResultContainer<Integer> res = new DomainResultContainer<Integer>();
					playerDAO.countPlayer(user, res).playOn(pm);
					if (res.get()==0) throw new NoPlayerException(user.getEmail());
				}
			},
			playerDAO.getIdByUser(user, playerId),
			new DomainRequest() {
			@Override
			public void playOn(PersistenceAccessor pm) throws DomainException {
				if (playerId.isEmpty())
					throw new NoPlayerException(user.getEmail());
				
				DomainQuery<GameCharacterStorable> query = pm.newQuery(GameCharacterStorable.class)
					.addCriteria("playerId", Long.class, playerId.get())
					.setOrdering("creationDate desc");
				
				List<GameCharacterStorable> queryResult = query.executeList();

				result.addAll(queryResult);
			}
		});

		return result;
	}

	/**
	 * Build the {@link DomainRequest} object for counting the {@link GameCharacterStorable} of the given {@link User}
	 * 
	 * @param playerId
	 * @param result
	 * @return
	 */
	DomainRequest countByOwnerRequest(
			final DomainResultContainer<Long> playerId, final DomainResultContainer<Integer> result) {
		return new DomainRequest(){
			@Override
			public void playOn(PersistenceAccessor pm) throws DomainException {
				DomainQuery<Integer> query0 = pm.newQuery(GameCharacterStorable.class)
					.withResult(Integer.class, "count(playerId)")
					.addCriteria("playerId", Long.class, playerId.get());
				result.set(query0.execute());
			}
		};
	}

	/**
	 * Build the {@link DomainRequest} object for counting the {@link GameCharacterStorable} with the given name for the given {@link User}
	 * 
	 * @param name
	 * @param playerId
	 * @param result
	 * @return the {@link DomainRequest} 
	 */
	DomainRequest countByNameAndOwnerRequest(final String name, final DomainResultContainer<Long> playerId,
			final DomainResultContainer<Integer> result) {
		return new DomainRequest(){
			@Override
			public void playOn(PersistenceAccessor pm) throws DomainException {
				DomainQuery<Integer> query1 = pm.newQuery(GameCharacterStorable.class)
					.withResult(Integer.class, "count(name)")
					.addCriteria("name", String.class, name)
					.addCriteria("playerId", Long.class, playerId.get());
				result.set(query1.execute());
			}
		};
	}

	/**
	 * Create a new {@link GameCharacterStorable} and store it in the DataStore
	 * 
	 * @param name the name of the new {@link GameCharacterStorable}
	 * @param gender the gender of the new {@link GameCharacterStorable}
	 * @param user the user owner of the new {@link GameCharacterStorable}
	 * @return the created {@link GameCharacterStorable}
	 * @throws DomainException of type<ul>
	 *    	<li> {@link GCMaxNumberCreateException} if {@link User} has reached is max number of {@link GameCharacterStorable}</li>
	 *      <li> {@link GCNameDuplicateCreateException} if the name given to </li>
	 *    </ul>
	 */
	public GameCharacterStorable create(final String name, final Gender gender, final User user) throws DomainException {
		final DomainResultContainer<Long> res0 = new DomainResultContainer<Long>();
		final DomainResultContainer<Integer> res1 = new DomainResultContainer<Integer>();
		final DomainResultContainer<Integer> res2 = new DomainResultContainer<Integer>();
		final DomainResultContainer<GameCharacterStorable> result = new DomainResultContainer<GameCharacterStorable>();
		eval(
			playerDAO.getIdByUser(user,res0),
			countByOwnerRequest(res0, res1),
			countByNameAndOwnerRequest(name, res0, res2),
			new DomainRequest(){
				@Override
				public void playOn(PersistenceAccessor pm) throws DomainException {
					if (res1.get() >= 3) {
						throw new GCMaxNumberCreateException(res1.get(),3);
					}
					if (res1.get() > 0) {
						throw new GCNameDuplicateCreateException(name);
					}
					 
					GameCharacterStorable gcSto = new GameCharacterStorable(
							null,
							name,
							gender,
							res0.get(),
							new Date(),
							new GameDate(gdm.getCurrent())
					);
					pm.makePersistent(gcSto);
					result.set(gcSto);
				}
			}
		);

		return result.get();
	}

	/**
	 * Delete a {@link GameCharacterStorable} for a given {@link User}
	 * 
	 * @param order the order id number of the character to be deleted
	 * @param user the owner user of the character
	 * @throws DomainException
	 */
	public void delete(final long order, final User user) throws DomainException {
		final DomainResultContainer<Long> result = new DomainResultContainer<Long>();
		eval(
			playerDAO.getIdByUser(user, result),
			new DomainRequest() {
			@Override
			public void playOn(PersistenceAccessor pm) throws DomainException {

				GameCharacterStorable gcSto = pm.getObjectById(GameCharacterStorable.class, order);

				if (result.get().equals(gcSto.getPlayerId())) {
					pm.deletePersistent(gcSto);
				} else {
					throw new DomainException("");
				}
			}
		});
	}

}
